/*
 * @Author: wuqingchun
 * @Date: 2024-05-21 20:25:31
 * @Description: 协程实现
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-05-24 09:43:43
 */
#include <stdio.h>
#include <ucontext.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/epoll.h>


#include "coroutine.h"

#include "co_connection.h"
#include "co_scheduler.h"

#define STACK_SIZE 64 * 1024 // 栈空间大小

ucontext_t g_astCoRTCtx[3];

ucontext_t g_stOtherCoRT;
ucontext_t g_stMainCoRT;

char g_acOtherStack[8192];

void coroutine_func1(void)
{
    printf("coroutine 1 is running ...\n");

    while (1)
    {
        printf("coroutine_func1\n");

        sleep(1);

        int iErr = swapcontext(&g_astCoRTCtx[1], &g_astCoRTCtx[2]);
        if (iErr == -1)
        {
            printf("swapcontext error, %s\n", strerror(errno));
        }
    }
}

void coroutine_func2(void)
{
    printf("coroutine 2 is running ...\n");

    while (1)
    {
        printf("coroutine_func2\n");

        sleep(1);

        int iErr = swapcontext(&g_astCoRTCtx[2], &g_astCoRTCtx[1]);
        if (iErr == -1)
        {
            printf("swapcontext error, %s\n", strerror(errno));
        }
    }
}

int test_raw_ucontext()
{
    char acStack1[STACK_SIZE];
    char acStack2[STACK_SIZE];

    printf("Starting ...\n");

    // 初始化主协程
    int iErr = getcontext(&g_astCoRTCtx[1]);
    if (iErr == -1)
    {
        printf("getcontext error, %s\n", strerror(errno));

        return 0;
    }

    g_astCoRTCtx[1].uc_stack.ss_sp = acStack1;
    g_astCoRTCtx[1].uc_stack.ss_size = sizeof(acStack1);  
    g_astCoRTCtx[1].uc_link = &g_astCoRTCtx[0]; // 设置返回时切换到主流程
    makecontext(&g_astCoRTCtx[1], coroutine_func1, 0);

    iErr = getcontext(&g_astCoRTCtx[2]);
    if (iErr == -1)
    {
        printf("getcontext error, %s\n", strerror(errno));

        return 0;
    }

    g_astCoRTCtx[2].uc_stack.ss_sp = acStack2;
    g_astCoRTCtx[2].uc_stack.ss_size = sizeof(acStack2);  
    g_astCoRTCtx[2].uc_link = &g_astCoRTCtx[0]; // 设置返回时切换到主流程
    makecontext(&g_astCoRTCtx[2], coroutine_func2, 0);


    // 首次运行主协程
    iErr = swapcontext(&g_astCoRTCtx[0], &g_astCoRTCtx[1]); // 切换到协程1
    if (iErr == -1)
    {
        printf("swapcontext error, %s\n", strerror(errno));

        return 0;
    }

    printf("main routine\n");

    return 0;
}

void coroutine1(void *pArg)
{
    if (pArg == nullptr)
    {
        return;
    }

    Coroutine* pobjCo = reinterpret_cast<Coroutine*>(pArg);
    printf("%s enter\n", __FUNCTION__);

    while (pobjCo->IsRunning())
    {
        printf("%s is running\n", __FUNCTION__);
        sleep(1);
        pobjCo->Yeild();
    }
    
    printf("%s exit\n", __FUNCTION__);
}

void test_coroutine()
{
    printf("%s enter\n", __FUNCTION__);

    ucontext_t stMainUCtx;
    volatile bool bRuning = true;

    Coroutine objCort(stMainUCtx, bRuning);

    int iErr = objCort.Init(1 << 16, coroutine1);
    if (iErr != 0)
    {
        return;
    }

    iErr = objCort.Resume();
    if (iErr == -1)
    {
        return;
    }

    int i = 0;
    while (i < 10)
    {
        printf("%s is running\n", __FUNCTION__);
        
        sleep(1);
        objCort.Resume();
        i++;
    }

    bRuning = false;
    printf("%s exit\n", __FUNCTION__);
}

int main()
{
    //test_coroutine();
    printf("Begin %s\n", __FUNCTION__);

    CoScheduler objScheduler;
    int iErr = objScheduler.Init();
    if (iErr != 0)
    {
        printf("CoScheduler Init error\n");
        return 0;
    }

    Connection* pobjConn = Connection::CreateListener();
    if (pobjConn == nullptr)
    {
        printf("CreateListener error\n");
        return 0;
    }

    iErr = objScheduler.AddConnection(*pobjConn, EPOLLIN);
    if (iErr != 0)
    {
        printf("AddConnection error\n");
        return 0;
    }

    bool bRunning = true;

    while (bRunning)
    {
        iErr = objScheduler.Schedule();
        if (iErr != 0)
        {
            printf("Schedule error, %s\n", strerror(errno));
            break;
        }
    }

    bRunning = false;

    printf("End %s\n", __FUNCTION__);

    return 0;
}
